#include <cstdlib>
#include <fstream>
#include <iostream>
#include "../constants.hpp"
#include "Properties.hpp"

using namespace BFT::membership;

// Singleton object declaration
boost::shared_ptr<Properties> Properties::_instance ;

#if defined(__MULTITHREAD__)
pthread_mutex_t Properties::_lock = PTHREAD_MUTEX_INITIALIZER ;
#endif

	
/**
 * Constructor
 * @param initFileName The configuration file name
 */
Properties::Properties(const std::string& initFileName) 
{
	try 
	{
		//fprintf(stderr,"trying to load init file %s\n",initFileName.c_str());
		load(initFileName) ;
	} 
	catch (PropertiesFailure ex) 
	{
		std::cerr << "ack" << std::endl;
	}

}


boost::shared_ptr<Properties> Properties::getInstance(const std::string& initFileName) 
{

// synchronized the access to this function
#if defined(__MULTITHREAD__)
	if ( pthread_mutex_lock(&_lock) != 0 ) 
	{
		return NULL ;
	}
#endif

    	if ( !_instance.get() ) 
    	{
		_instance.reset( new Properties(initFileName) ) ;
	} 
	else 
	{
		//????
	}
	
	// unlock
#if defined(__MULTITHREAD__)
		pthread_mutex_unlock(&_lock) ;
#endif

	return _instance ;
}


void Properties::refresh() 
{
	clear();
    	_instance.reset() ;
}


void Properties::load(const std::string& initFileName) throw (PropertiesFailure) 
{

	std::ifstream in( initFileName.c_str() );

        if (!in) 
        {
        	throw PropertiesFailure(std::string("File ") + initFileName + " does not exist");
        }

        clear();
        std::string fullLine, command;
        std::string leftSide, rightSide;
        char line[CONF_LINE_LENGTH];
        std::string::size_type length;

        while (in.getline(line, CONF_LINE_LENGTH)) 
        {
        	fullLine = line;
		//fprintf(stderr,"line: %s\n",fullLine.c_str());
		/* if the line contains a # then it is a comment
                if we find it anywhere other than the beginning, then we assume 
                there is a command on that line, and it we don't find it at all
                we assume there is a command on the line (we test for valid 
		command later) if neither is true, we continue with the next line
		*/
		length = fullLine.find('#');
		if (length == std::string::npos) 
		{
                	command = fullLine;
            	} 
            	else if (length > 0) 
            	{
                	command = fullLine.substr(0, length);
            	} 
            	else 
            	{
                	continue;
            	}

            	// check the command and handle it
            	length = command.find('=');
            	if (length != std::string::npos) 
            	{
                	leftSide = command.substr(0, length);
                	rightSide = command.substr(length + 1, command.size() - length);
            	} 
            	else 
            	{
                	continue;
            	}

	    	// clean trailing white space
	    	int index = leftSide.find_last_not_of(" ");
	    	leftSide.erase(index+1, leftSide.size());

	    	// clean leading white space
	    	index = rightSide.find_first_not_of(" ");
	    	rightSide.erase(0,index);

            	// add to the map of properties
            	insert(value_type(leftSide, rightSide));
	}

		// close the file
	in.close() ;
}

void Properties::save(const std::string& outputFileName) throw (PropertiesFailure) 
{
	std::ofstream out(outputFileName.c_str());

        if (!out) 
        {
        	throw PropertiesFailure(std::string("File ") + outputFileName + " already exists");
        }

        for(const_iterator i = begin(); i != end(); ++i) 
        {
        	out << (*i).first << "=" << (*i).second << std::endl;
        }
	
	out.close() ;
}
    
int Properties::getInt(const std::string& property, int defaultValue) 
{
	const_iterator key = find(property);
	return (key == end()) ? defaultValue : std::atoi((*key).second.c_str());
}

bool Properties::getBool(const std::string& property, bool defaultValue) 
{
	const_iterator key = find(property);
	return (key == end()) ? defaultValue : ((*key).second == "true");
}

std::string Properties::getString(const std::string& property, const char* defaultValue) {
        const_iterator key = find(property);
        return (key == end()) ? std::string(defaultValue) : (*key).second;
}

void Properties::printInfo() 
{
	fprintf(stdout,"Properties infromation:\n");
       	for(const_iterator i = begin(); i != end(); ++i) 
        {
        	fprintf(stdout,"Name: %s, Value:%s\n",(*i).first.c_str(),(*i).second.c_str());
        }
	
}
